<?php
/**
 * SocialEngine
 *
 * @category   Application_Core
 * @package    Storage
 * @copyright  Copyright 2006-2010 Webligo Developments
 * @license    http://www.socialengine.net/license/
 * @version    $Id: S3.php 8800 2011-04-06 02:09:28Z john $
 * @author     John Boehr <j@webligo.com>
 */

/**
 * @category   Application_Core
 * @package    Storage
 * @copyright  Copyright 2006-2010 Webligo Developments
 * @license    http://www.socialengine.net/license/
 */
class Storage_Service_S3 extends Storage_Service_Abstract {
	// General
	

	protected $_type = 's3';
	
	protected $_path;
	
	protected $_baseUrl;
	
	/**
	 * @var Zend_Service_Amazon_S3
	 */
	protected $_internalService;
	
	protected $_bucket;
	
	protected $_streamWrapperName;
	
	public function __construct(array $config) {
		if (empty ( $config ['bucket'] )) {
			throw new Storage_Service_Exception ( 'No bucket specified' );
		}
		$this->_bucket = $config ['bucket'];
		$this->_internalService = new Zend_Service_Amazon_S3 ( $config ['accessKey'], $config ['secretKey'], $config ['region'] );
		
		if (! empty ( $config ['path'] )) {
			$this->_path = $config ['path'];
		} else {
			$this->_path = 'public';
		}
		
		if (! empty ( $config ['baseUrl'] )) {
			$this->_baseUrl = $config ['baseUrl'];
			unset ( $config ['baseUrl'] );
			// Add http:// if no protocol
			if (false === strpos ( $this->_baseUrl, '://' )) {
				$this->_baseUrl = 'http://' . $this->_baseUrl;
			}
		}
		
		// Should we register the stream wrapper?
		$this->_streamWrapperName = 's3' . ( int ) @$config ['service_id'];
		$this->_internalService->registerStreamWrapper ( $this->_streamWrapperName );
		
		parent::__construct ( $config );
	}
	
	public function getType() {
		return $this->_type;
	}
	
	/**
	 * Returns a url that allows for external access to the file. May point to some
	 * adapter which then retrieves the file and outputs it, if desirable
	 *
	 * @param Storage_Model_DbRow_File The file for operation
	 * @return string
	 */
	public function map(Storage_Model_File $model) {
		// Remove bucket from storage path? (b/c)
		$path = $model->storage_path;
		if (substr ( $path, 0, strlen ( $this->_bucket ) + 1 ) == $this->_bucket . '/') {
			$path = ltrim ( substr ( $path, strlen ( $this->_bucket ) + 1 ), '/' );
		}
		
		// Make url
		if (! $this->_baseUrl) {
			// Map to S3 bucket directly
			return 'http://' . $this->_bucket . '.s3.amazonaws.com/' . $path;
		} else {
			// Map to baseUrl (cloudfront)
			return rtrim ( $this->_baseUrl, '/' ) . '/' . $path;
		}
	}
	
	/**
	 * Stores a local file in the storage service
	 *
	 * @param Zend_Form_Element_File|array|string $file Temporary local file to store
	 * @param array $params Contains iden
	 * @return string Storage type specific path (internal use only)
	 */
	public function store(Storage_Model_File $model, $file) {
		$path = $this->getScheme ()->generate ( $model->toArray () );
		
		// Prefix path with bucket?
		//$path = $this->_bucket . '/' . $path;
		

		// Copy file
		try {
			$return = $this->_internalService->putFile ( $file, $this->_bucket . '/' . $path, array (Zend_Service_Amazon_S3::S3_ACL_HEADER => Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ, 'Cache-Control' => 'max-age=864000, public' ) );
			if (! $return) {
				throw new Storage_Service_Exception ( 'Unable to store file.' );
			}
		} catch ( Exception $e ) {
			throw $e;
		}
		
		return $path;
	}
	
	/**
	 * Returns the content of the file
	 *
	 * @param Storage_Model_DbRow_File $model The file for operation
	 * @param array $params
	 */
	public function read(Storage_Model_File $model) {
		$path = $this->_bucket . '/' . $model->storage_path;
		
		try {
			$response = $this->_internalService->getObject ( $path );
			if (! $response) {
				throw new Storage_Service_Exception ( 'Unable to write file.' );
			}
		} catch ( Exception $e ) {
			throw $e;
		}
		
		return $response;
	}
	
	/**
	 * Creates a new file from data rather than an existing file
	 *
	 * @param Storage_Model_DbRow_File $model The file for operation
	 * @param string $data
	 */
	public function write(Storage_Model_File $model, $data) {
		$path = $this->getScheme ()->generate ( $model->toArray () );
		
		// Prefix path with bucket?
		//$path = $this->_bucket . '/' . $path;
		

		// Copy file
		try {
			$return = $this->_internalService->putObject ( $this->_bucket . '/' . $file, $data, array (Zend_Service_Amazon_S3::S3_ACL_HEADER => Zend_Service_Amazon_S3::S3_ACL_PUBLIC_READ, 'Cache-Control' => 'max-age=864000, public' ) );
			if (! $return) {
				throw new Storage_Service_Exception ( 'Unable to write file.' );
			}
		} catch ( Exception $e ) {
			throw $e;
		}
		
		return $path;
	}
	
	/**
	 * Removes the file
	 *
	 * @param Storage_Model_DbRow_File $model The file for operation
	 */
	public function remove(Storage_Model_File $model) {
		if (! empty ( $model->storage_path )) {
			$path = $this->_bucket . '/' . $model->storage_path;
			try {
				$return = $this->_internalService->removeObject ( $path );
				if (! $return) {
					throw new Storage_Service_Exception ( 'Unable to remove file.' );
				}
			} catch ( Exception $e ) {
				throw $e;
			}
		}
	}
	
	/**
	 * Creates a local temporary local copy of the file
	 *
	 * @param Storage_Model_DbRow_File $model The file for operation
	 */
	public function temporary(Storage_Model_File $model) {
		if (substr ( $model->storage_path, 0, strlen ( $this->_bucket ) ) == $this->_bucket) {
			$path = $model->storage_path;
		} else {
			$path = $this->_bucket . '/' . $model->storage_path;
		}
		
		try {
			$rfh = fopen ( $this->_streamWrapperName . '://' . $path, 'r' );
		} catch ( Exception $e ) {
			throw $e;
		}
		
		$tmp_file = APPLICATION_PATH . '/public/temporary/' . basename ( $model ['storage_path'] );
		$fp = fopen ( $tmp_file, "w" );
		stream_copy_to_stream ( $rfh, $fp );
		fclose ( $fp );
		@chmod ( $tmp_file, 0777 );
		return $tmp_file;
	}
	
	public function removeFile($path) {
		// Should we add bucket here?
		$path = $this->_bucket . '/' . $path;
		$this->_internalService->removeObject ( $path );
	}
}